/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief PNG decoder interface using the libpng library.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#ifndef _PNGDECODER_HPP_
#define _PNGDECODER_HPP_
#include <stdio.h>
#include <png.h>
#include <setjmp.h>

/**
 * PNG decoder interface using the libjpeg (from IJG)
 *
 * This is CPNGDecoder version 1.0.
 * 
 *
 * Version 1.0 (9.7.2007)
 * - Initial implementation of the PNGDecoder interface. Provides
 *   basic facilities to .. surprisingly enough .. Decode PNG images.
 *
 */
class CPNGDecoder 
{
public:

  /**
   * Default constructor. If called with parameters, the assigned
   * pointer is assigned as PNG source. See 'SetPNGDataPtr' for more
   * info.
   * 
   * @param[in] aData           Pointer to PNG data to decode.
   * @param[in] aDataLen        Maximum available datasize in this buffer.
   */
  CPNGDecoder(const char *aData = NULL, const int aDataLen = 0, bool aCopy = false);


  /**
   * Default destructor.
   */
  ~CPNGDecoder();
  

  /**
   * Assigns a pointer to read data from. NOTE: This only stores the
   * datasource pointer. Therefore, you must NOT destroy or modify the
   * provided pointer while using the instance. \See ReleasePNGDataPtr
   * for more info. 
   * The SetPNGDataPtr can be called again right after the user has
   * read all the data needed. (Image sizes, RGB data etc). This means that
   * the CPNGDecoder class is reusable, and doesn't have to be reallocated
   * every time new image is processed. (In fact its recommended to NOT
   * construct a new CPNGDecoder object for every new image, because the 
   * internal RGB buffer
   * is preserved across calls, saving the time used for dynamic allocation)
   *
   * @param[in] aData           Pointer to PNG data to decode.
   * @param[in] aDataLen        Maximum available datasize in this buffer.
   * @param[in] aCopy           'true' - Duplicate provided PNG data; 'false' 
   *                            - point to original data region (region must stay
   *                            intact until decoding is complete)
   * @return                    true == success, false == failure. Note that
   *                            no analysis is done on the data itself
   *                            during this call.
   *
   */
  bool SetPNGDataPtr(const char *aData, const int aDataLen, bool aCopy = false);


  /** This function requests to Decode header only.
   *
   * Call this function before attempting to read any image parameters
   * like width, height etc.
   *
   * @return                    'true' if header was succesfully decoded,
   *                            'false' otherwise.
   */
  bool DecodeHeader(void);


  /** This function requests a Decode for the whole image data.
   *
   * Calling this function decodes the currently assigned PNG buffer
   * to internal RGB buffer. After this call has returned, possible
   * external PNG datapointers can be released
   *
   * @return                    'true' if decoding was succesfull, and data
   *                            is now fetchable by GetRGBDataPtr or
   *                            GetRGBDataCopy functions.
   */
  bool Decode(void);


  /**
   * This function returns a pointer to beginning of RGB data. (In format
   * <red><green><blue><red><green>...) All single color components are 8 bits.
   * The pointer points to internal buffer, so modifying the returned buffer
   * is not allowed. Also, while using the data the instance must be kept
   * alive. Also, calling the SetPNGDataPtr invalidates the pointer.
   * 
   * @return                    Pointer to RGB data or NULL on failure.
   *
   */
  const char *GetRGBDataPtr(void);

  /**
   * Works just like \see GetRGBDataPtr, except that the data is copied to
   * separate user-supplied buffer. This avoids buffer invalidation in case of
   * changing ptr.
   * 
   * @param[in] aDataPtr        Pointer to memory location to copy the RGB data.
   * @param[in] aMaxBufSize     Buffer size available. (May not be used completely,
   *                            but atleast the number of bytes returned by
   *                            GetRGBDataSize(void) is required)
   * @return                    true == success. Image Decompress was succesfull,
   *                            and the data is now stored to user supplied pointer.
   *                            false == failure. Image decompression failed, or 
   *                            user supplied buffer was too small.
   *
   */ 
  bool GetRGBDataCopy(char *aDataPtr, const unsigned int aMaxBufSize);


  /**
   * Returns the number of bytes required by RGB data. 
   * 
   * @return                    Positive number of bytes if PNG Header was 
   *                            valid, otherwise -1 on error.
   *  
   */
  int GetRGBDataSize(void);


  /** Return pointer to decoded buffer, whatever format that may be.
   *
   * Pointer to internal decoded (raw) buffer. If any. Please note
   * that this pointer is pointer to an internal buffer, and hence the
   * addressed object must remain valid as long as this pointer is
   * addressed in the application.
   *
   * @return A pointer value if buffer exists (An PNG was previously
   * succesfully decoded), or NULL if nothing decoded or decoding has
   * previously failed.
   */
  const char *GetRawDataPtr(void);

  /** Get a copy of the raw (decoded) buffer.
   *
   * This function is essentially the same as GetRawDataPtr(), except
   * that it copies the data to user specified buffer instead of
   * returning a pointer to internal buffer.
   *
   * @param[out] aDataPtr Pointer where to copy the data
   * @param[in] aMaxBufSize Buffer size available. If this is smaller
   * than the data available, an error is returned. (Note, the real
   * size is available through the GetRawDataSize() function)
   *
   * @return 'true' if valid data was available, and was successfully
   * coped to an user provided, adequately sized buffer. Otherwise
   * 'false'.
   */
  bool GetRawDataCopy(char *aDataPtr, const unsigned int aMaxBufSize);


  /** Return the size of the Raw data.
   *
   * @return Positive number of bytes if PNG Header was valid,
   *         otherwise -1 on error.
   */
  int GetRawDataSize(void);
  

  /** Return color type of buffer.
   *
   * @return Color type as libpng constant (one of PNG_COLOR_TYPE_* constants),
   * or negative on error (no image decoded or header not decoded)
   */
  int GetColorType(void);


  /** Return bit depth of decoded (raw) data.
   *
   * @return Bit depth in bits, for example: 8 - or negative on error
   * (no image decoded or header not decoded)
   */
  int GetBitDepth(void);


  /**
   * Returns the image width in pixels. 
   *
   * @return                    Positive number of pixels if PNG header was
   *                            valid, otherwise -1 on error.
   */
  int GetImageWidth(void);


  /**
   * Returns the image height in pixels. 
   *
   * @return                    Positive number of pixels if PNG header was
   *                            valid, otherwise -1 on error.
   */
  int GetImageHeight(void);

private:
  bool InitDecode(void);

  void ReservePNGData(const unsigned int aSizeBytes);
  void ReserveRawData(const unsigned int aSizeBytes);

private:
  // Encoded PNG data pointer here.
  char *iPNGData;
  unsigned int iPNGDataSize;
  unsigned int iPNGDataReserved;
  bool iPNGDataOwned;

  // Decoded RGB data here.
  char *iRawData;
  unsigned int iRawDataSize;
  unsigned int iRawDataReserved;

  // PNG Stuff.
  png_structp iPNGDecoderPtr;
  png_infop iPNGInfoPtr;

  // Status flags
  bool iGotHeader;
  bool iGotRaw;

  // Decoded information
  unsigned int iWidth;
  unsigned int iHeight;
  int iColorType;
  int iBitDepth;

private:
  static void ReadDataFromInputStream(png_structp png_ptr, 
                                      png_bytep outBytes,
                                      png_size_t byteCountToRead);
  unsigned int iReadDataIndex;
  
private:

  // Private constructors (Copying of PNGDecoder object denied)
  CPNGDecoder(const CPNGDecoder &)
    : iPNGData(),
      iPNGDataSize(),
      iPNGDataReserved(),
      iPNGDataOwned(),
      iRawData(),
      iRawDataSize(),
      iRawDataReserved(),
      iPNGDecoderPtr(),
      iPNGInfoPtr(),
      iGotHeader(),
      iGotRaw(),
      iWidth(),
      iHeight(),
      iColorType(),
      iBitDepth(),
      iReadDataIndex() {}
  CPNGDecoder &operator =(const CPNGDecoder &) { return *this; }
};
#endif
